<template>
  <canvas ref="canvas" class="canvas-bg"></canvas>
</template>

<script>
export default {
  name: 'CanvasBg',
  data() {
    return {
      // 鼠标跟随相关变量
      mouseX: 0,
      mouseY: 0,
      targetX: 0,
      targetY: 0,
      animationId: null
    };
  },
  mounted() {
    this.initCanvas();
  },
  beforeUnmount() {
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
    }
    window.removeEventListener('resize', this.handleResize);
    // 在document上移除鼠标事件监听器
    document.removeEventListener('mousemove', this.handleMouseMove);
  },
  methods: {
    initCanvas() {
      const PHI = (1 + Math.sqrt(5)) / 2; // 1.618033988749895
      const maxGeneration = (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) ? 5 : 6;
      const frameDuration = 1000 / 60;
      const duration = 3000;
      const rotationSpeed = 0.3;
      const totalIterations = Math.floor(duration / frameDuration);
      const maxBaseSize = 300;
      const baseSizeSpeed = 0.02;

      const canvas = this.$refs.canvas;
      const ctx = canvas.getContext("2d");
      let canvasWidth = document.documentElement.clientWidth;
      let canvasHeight = document.documentElement.clientHeight;
      let shapes = [];
      let sizeVariation;
      let iteration = 0;
      let animationDirection = 1;
      const sizeVariationRange = 0.15;
      let baseRotation = 0;
      let baseSize = 200;
      let c1 = 43;
      let c1S = 1;
      let c2 = 205;
      let c2S = 1;
      let c3 = 255;
      let c3S = 1;
      const easing = 0.05;

      // 初始化鼠标位置
      this.mouseX = canvasWidth / 2;
      this.mouseY = canvasHeight / 2;
      this.targetX = canvasWidth / 2;
      this.targetY = canvasHeight / 2;

      canvas.setAttribute("width", canvasWidth);
      canvas.setAttribute("height", canvasHeight);

      function Shape(gen, x, y, size, rotation) {
        this.generation = gen;
        this.size = size;
        this.rotation = -rotation;
        this.start = {
          x: x,
          y: y
        };
        this.end = {
          x_1: this.start.x + Math.cos(degToRad(this.rotation)) * this.size,
          y_1: this.start.y + Math.sin(degToRad(this.rotation)) * this.size,
          x_2: this.start.x + Math.cos(degToRad(this.rotation + 360 / 3)) * this.size,
          y_2: this.start.y + Math.sin(degToRad(this.rotation + 360 / 3)) * this.size,
          x_3: this.start.x + Math.cos(degToRad(this.rotation + 360 / 3 * 2)) * this.size,
          y_3: this.start.y + Math.sin(degToRad(this.rotation + 360 / 3 * 2)) * this.size
        };

        this.init();
      }

      Shape.prototype.init = function () {
        if (this.generation < maxGeneration) {
          const gen = this.generation + 1;
          const newSize = this.size * sizeVariation;
          const newRotation = this.rotation;

          shapes.push(new Shape(gen, this.end.x_1, this.end.y_1, newSize, newRotation));
          shapes.push(new Shape(gen, this.end.x_2, this.end.y_2, newSize, newRotation));
          shapes.push(new Shape(gen, this.end.x_3, this.end.y_3, newSize, newRotation));
        }
        this.draw();
      };

      Shape.prototype.draw = function () {
        ctx.beginPath();
        ctx.moveTo(this.start.x, this.start.y);
        ctx.lineTo(this.end.x_1, this.end.y_1);
        ctx.moveTo(this.start.x, this.start.y);
        ctx.lineTo(this.end.x_2, this.end.y_2);
        ctx.moveTo(this.start.x, this.start.y);
        ctx.lineTo(this.end.x_3, this.end.y_3);
        ctx.strokeStyle = "rgba(" + c1 + "," + c2 + "," + c3 + "," + 1 / this.generation / 5 + ")";
        ctx.stroke();
      };

      const animate = () => {
        ctx.globalCompositeOperation = "source-over";
        ctx.fillStyle = "rgba(0,0,0,.1)";
        ctx.fillRect(0, 0, canvasWidth, canvasHeight);
        ctx.globalCompositeOperation = "lighter";
        
        // 缓动跟随鼠标
        this.mouseX += (this.targetX - this.mouseX) * easing;
        this.mouseY += (this.targetY - this.mouseY) * easing;
        
        shapes = [];
        shapes.push(new Shape(0, this.mouseX, this.mouseY, baseSize, baseRotation));

        changeColor();
        iteration++;
        if (baseSize < maxBaseSize) baseSize += baseSizeSpeed;
        baseRotation += rotationSpeed;
        sizeVariation = easeInOutSine(
          iteration,
          1 - sizeVariationRange * animationDirection,
          sizeVariationRange * 2 * animationDirection,
          totalIterations
        );
        if (iteration >= totalIterations) {
          iteration = 0;
          animationDirection *= -1;
        }
        this.animationId = requestAnimationFrame(animate);
      };

      function degToRad(deg) {
        return Math.PI / 180 * deg;
      }

      function easeInOutSine(currentIteration, startValue, changeInValue, totalIterations) {
        return (
          changeInValue / 2 * (1 - Math.cos(Math.PI * currentIteration / totalIterations)) + startValue
        );
      }

      function changeColor() {
        if (c1 == 0 || c1 == 255) c1S *= -1;
        if (c2 == 0 || c2 == 255) c2S *= -1;
        if (c3 == 0 || c3 == 255) c3S *= -1;
        c1 += 1 * c1S;
        c2 += 1 * c2S;
        c3 += 1 * c3S;
      }

      // 鼠标移动事件处理 - 在document上监听，获取全局鼠标位置
      this.handleMouseMove = (e) => {
        this.targetX = e.clientX;
        this.targetY = e.clientY;
      };

      // 窗口大小调整事件处理
      this.handleResize = () => {
        canvasWidth = document.documentElement.clientWidth;
        canvasHeight = document.documentElement.clientHeight;
        canvas.setAttribute("width", canvasWidth);
        canvas.setAttribute("height", canvasHeight);
        ctx.strokeStyle = "rgba(66,134,240,.3)";
        ctx.globalCompositeOperation = "lighter";
        
        // 重新设置鼠标初始位置为中心
        this.mouseX = canvasWidth / 2;
        this.mouseY = canvasHeight / 2;
        this.targetX = canvasWidth / 2;
        this.targetY = canvasHeight / 2;
      };

      // 在document上添加鼠标事件监听器，这样不会被其他元素阻挡
      document.addEventListener("mousemove", this.handleMouseMove);
      window.addEventListener("resize", this.handleResize);

      ctx.globalCompositeOperation = "lighter";
      animate();
    }
  }
};
</script>

<style scoped>
.canvas-bg {
  background-color: black;
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: -1;
  pointer-events: none;
}
</style>